home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xarchie-2.0.9 / ftp.c < prev    next >
C/C++ Source or Header  |  1995-06-18  |  47KB  |  1,680 lines

  1. /*
  2.  * ftp.c : This code implements an asynchronous ftp client.
  3.  *
  4.  * George Ferguson, ferguson@cs.rochester.edu, 23 Apr 1993.
  5.  *
  6.  * The client is represented by an instance of the FtpContext structure,
  7.  * which includes the state of the client with respect to a finite-state
  8.  * model of the FTP process. All aspects of the connection are contained
  9.  * in the structure, so a process can have multiple outstanding FTP
  10.  * sessions (subject to filesystem limitations, of course).
  11.  *
  12.  * Refer to RFC959 for details of the state model.
  13.  *
  14.  * Two external functions must be provided:
  15.  *   void RegisterFtpFd( ftpc, fd, flags, func )
  16.  *      Should arrange to call FUNC passing FTPC whenever FD is ready
  17.  *      as indicated by FLAGS (from <fcntl.h>).
  18.  *   void UnregisterFtpFd( ftpc, fd )
  19.  *      Cancels a previous registration of FD for FTPC.
  20.  * Samples of these using select(2) are given below in the STANDALONE
  21.  * section. Xarchie uses the XtAppAddInput() mechanism. The code also
  22.  * uses alert0() and status0() for messages, sysError() for error
  23.  * messages from system calls, and ftpPrompt() if prompting is enabled.
  24.  *
  25.  * To use these routines:
  26.  *   ftpc = ftpNewContext(...);
  27.  *   ftpStart(ftpc);
  28.  * The function you registered as the "done" parameter in ftpNewContext
  29.  * will be called back when the process finishes (for any reason). You
  30.  * should call ftpFreeContext() in this callback (or register it as the
  31.  * callback, or register NULL).
  32.  *
  33.  * This code is decidely *not* a general-purpose FTP library. It is
  34.  * designed to support the batch transfers required by xarchie. I suppose
  35.  * that it could be modified to be more general without too much effort.
  36.  * In particular, many of the routines called by ftpProcessReply could
  37.  * be made into external interface functions. Volunteers?
  38.  *
  39.  * 28 Apr 1993: Status mesage for bytes transferred was backwards.
  40.  *  1 Jun 1993: Fixes for ISC. Are these needed for other SYS5?
  41.  * 30 Jun 1993: Fixes for Encore Umax (used cpp symbol "umax" as a guess).
  42.  * 24 Aug 1993: Allow 200 reply for CWD as well as 250.
  43.  *              Non-blocking IO defs for NeXT.
  44.  */
  45. #include <stdio.h>
  46. #include <errno.h>
  47. #ifndef MSDOS
  48. extern int errno;    /* Encore needs this despite <errno.h> */
  49. #endif
  50. #include "config.h"
  51. #ifdef HAVE_SYS_PARAM_H
  52. #include <sys/param.h>
  53. #endif
  54. #include "ftp.h"    /* includes <sys/types.h> and <netinet/in.h> */
  55. #include "sysdefs.h"    /* char *malloc() */
  56. #include "stringdefs.h" /* char *strcpy(), *index(); */
  57. #include "debug.h"    /* MSG[0-3]() macros */
  58.  
  59. /* These functions have to be provided: */
  60. extern void RegisterFtpFd(/* ftpc, fd, O_RDWR | O_RDONLY, func */);
  61. extern void UnregisterFtpFd(/* ftpc, fd */);
  62. extern int ftpPrompt(/* ftpc */);
  63. extern void status0(/* str */);
  64. extern void alert0(/* str */);
  65. extern void sysError(/* str */);
  66.  
  67. /*
  68.  * Portable non-blocking I/O macros, I hope.
  69.  *   hp300 : From Andy.Linton@comp.vuw.ac.nz
  70.  *   sgi   : From amoss@cs.huji.ac.il
  71.  *   umax  : From Gerry.Tomlinson@newcastle.ac.uk
  72.  *   NeXT  : From jk@zarniwoop.pc-labor.uni-bremen.de
  73.  */
  74. #if defined(hp300) || defined(NeXT)
  75.   /* Here's for BSD, maybe, but Sys5 can't tell this from EOF. */
  76. # include <fcntl.h>
  77. # define MAKE_NONBLOCKING(fd) fcntl(fd,F_SETFL,O_NDELAY)
  78. # define ITWOULDBLOCK EWOULDBLOCK
  79. #else
  80. #if defined(sgi)
  81. # include <fcntl.h>
  82. # define MAKE_NONBLOCKING(fd) fcntl(fd,F_SETFL,O_NONBLOCK)
  83. # define ITWOULDBLOCK EWOULDBLOCK
  84. #else
  85. #if defined(umax)
  86. # define MAKE_NONBLOCKING(fd) fcntl(fd,F_SETFL,FNDELAY)
  87. # define ITWOULDBLOCK EWOULDBLOCK
  88. #else
  89.   /* This is POSIX, the default, which uses EAGAIN. */
  90. # include <fcntl.h>
  91. # define MAKE_NONBLOCKING(fd) fcntl(fd,F_SETFL,O_NONBLOCK)
  92. # define ITWOULDBLOCK EAGAIN
  93. #endif /* umax */
  94. #endif /* sgi */
  95. #endif /* hp300 */
  96. /*
  97.  * Networking includes from Brendan Kehoe (dirsend.c)
  98.  */
  99. #include <netdb.h>
  100. #include <sys/socket.h>
  101. #ifndef hpux
  102. # include <arpa/inet.h>
  103. #endif
  104. /* Interactive UNIX keeps some of the socket definitions in funny places.  */
  105. #ifdef ISC
  106. # include <net/errno.h>        /* I wonder about this... */
  107. #endif /* ISC */
  108.  
  109. #ifndef IPPORT_FTP
  110. #define IPPORT_FTP 21        /* From <netinet/in.h> */
  111. #endif
  112.  
  113. #define NewString(S)        strcpy(malloc(strlen(S)+1),S)
  114. #define ftpfilecmdstr(X)    ((X)==FTP_GET ? "GET" : "PUT")
  115.  
  116. /*
  117.  * Functions defined here:
  118.  */
  119. FtpContext *ftpNewContext();
  120. void ftpStart(),ftpAbort();
  121. void ftpFreeContext();
  122.  
  123. /* Initialization */
  124. static int ftpGetHostAndPort();
  125. /* Connection establishment */
  126. static void ftpConnectCallback();
  127. static int ftpGetSocket(),ftpConnect();
  128. /* Reading server replies */
  129. static void ftpInitReply(),ftpReplyCallback();
  130. /* Main FTP client state-machine */
  131. static void ftpProcessReply();
  132. static void ftpAlert(),ftpHandleErrorThenQuit(),ftpDone();
  133. static void ftpSendType(),ftpSendNextCwd();
  134. static int ftpSendPort();
  135. static void ftpSendGetPut();
  136. static int ftpAcceptDataConn(),ftpSetupLocalData();
  137. /* Data connection processing */
  138. static void ftpDataCallback();
  139. static void ftpReadData(),ftpWriteData(),ftpCleanupDataConn();
  140. /* Sending commands to the server */
  141. static void ftpStartSendingCmd(),ftpWriteCallback();
  142. static void ftpSendCmd(),ftpSendAbort();
  143. #ifdef DEBUG
  144. char *ftpstatestr();
  145. #endif
  146.  
  147. /*    -    -    -    -    -    -    -    -    */
  148. /* Exported functions: */
  149.  
  150. /*
  151.  * Returns a pointer to a new FtpContext for which the connection is
  152.  * started. The external function RegisterFtpFd() is used to register
  153.  * the control socket for subsequent processing by ftpProcess().
  154.  */
  155. FtpContext *
  156. ftpNewContext(hostname,user,pass,cwd,local_dir,type,stripcr,prompt,
  157.           filecmd,files,num_files,trace,done1,done)
  158. char *hostname,*user,*pass,*cwd,*local_dir;
  159. int type,stripcr,prompt,filecmd;
  160. char **files;
  161. int num_files;
  162. FtpTraceProc trace;
  163. FtpCallbackProc done1,done;
  164. {
  165.     FtpContext *ftpc;
  166.     int i;
  167.  
  168.     /* Initialize the data structure */
  169.     if ((ftpc=(FtpContext *)malloc(sizeof(FtpContext))) == NULL) {
  170.     fprintf(stderr,"ftpNewContext: malloc failed\n");
  171.     return(NULL);
  172.     }
  173.     /* User-specified stuff */
  174.     ftpc->hostname = NewString(hostname);
  175.     ftpc->user = NewString(user);
  176.     ftpc->pass = NewString(pass);
  177.     ftpc->cwd = NewString(cwd);
  178.     ftpc->wd = ftpc->cwd;
  179.     ftpc->local_dir = NewString(local_dir);
  180.     ftpc->type = type;
  181.     ftpc->stripcr = stripcr;
  182.     ftpc->prompt = prompt;
  183.     ftpc->filecmd = filecmd;
  184.     ftpc->num_files = num_files;
  185.     ftpc->files = (char **)calloc(num_files,sizeof(char *));
  186.     for (i=0; i < num_files; i++)
  187.     ftpc->files[i] = NewString(files[i]);
  188.     ftpc->this_file = -1;    /* preincrement later */
  189.     ftpc->this_size = 0;
  190.     ftpc->trace = trace;
  191.     ftpc->done1 = done1;
  192.     ftpc->done = done;
  193.     /* Default stuff */
  194.     ftpc->reply[0] = '\0';
  195.     ftpc->stripcr = 1;
  196.     /* Undefined stuff */
  197.     ftpc->ctrl = ftpc->data = ftpc->port = ftpc->local_fd = -1;
  198.     ftpc->state = ftpc->iostate = ftpc->reply_state = ftpc->saved_state = 0;
  199.     ftpc->retcode = ftpc->tmpcode = 0;
  200.     ftpc->cmd = NULL;
  201.     /* Go start the connection */
  202.     if (ftpGetHostAndPort(ftpc) < 0) {
  203.     ftpFreeContext(ftpc);
  204.     return(NULL);
  205.     }
  206.     ftpc->state = 0;
  207.     return(ftpc);
  208. }
  209.  
  210. void
  211. ftpFreeContext(ftpc)
  212. FtpContext *ftpc;
  213. {
  214.     int i;
  215.  
  216.     DEBUG1("ftpFreeContext: ftpc=0x%x\n",ftpc);
  217.     if (ftpc->ctrl != -1) {
  218.     UnregisterFtpFd(ftpc,ftpc->ctrl);
  219.     DEBUG1("ftpFreeContext: closing ctrl %d\n",ftpc->ctrl);
  220.     close(ftpc->ctrl);
  221.     }
  222.     if (ftpc->data != -1) {
  223.     UnregisterFtpFd(ftpc,ftpc->data);
  224.     DEBUG1("ftpFreeContext: closing data %d\n",ftpc->data);
  225.     close(ftpc->data);
  226.     }
  227.     if (ftpc->port != -1) {
  228.     DEBUG1("ftpFreeContext: closing port %d\n",ftpc->port);
  229.     close(ftpc->port);
  230.     }
  231.     if (ftpc->local_fd != -1) {
  232.     DEBUG1("ftpFreeContext: closing local_fd %d\n",ftpc->local_fd);
  233.     close(ftpc->local_fd);
  234.     }
  235.     if (ftpc->hostname)
  236.     free(ftpc->hostname);
  237.     if (ftpc->user)
  238.     free(ftpc->user);
  239.     if (ftpc->pass)
  240.     free(ftpc->pass);
  241.     if (ftpc->cwd)
  242.     free(ftpc->cwd);
  243.     if (ftpc->local_dir)
  244.     free(ftpc->local_dir);
  245.     if (ftpc->files) {
  246.     for (i=0; i < ftpc->num_files; i++)
  247.         if (ftpc->files[i]) free(ftpc->files[i]);
  248.     free((char *)(ftpc->files));
  249.     }
  250.     if (ftpc->h_addr_list)
  251.     free((char *)(ftpc->h_addr_list));
  252.     DEBUG0("ftpFreeContext: done\n");
  253. }
  254.  
  255. void
  256. ftpStart(ftpc)
  257. FtpContext *ftpc;
  258. {
  259.     DEBUG1("ftpStart: ftpc=0x%x\n",ftpc);
  260.     ftpc->state = FTPS_OPEN;
  261.     ftpConnectCallback(ftpc);        /* get things started */
  262.     DEBUG0("ftpStart: done\n");
  263. }
  264.  
  265. void
  266. ftpAbort(ftpc)
  267. FtpContext *ftpc;
  268. {
  269.     DEBUG1("ftpAbort: ftpc=0x%x\n",ftpc);
  270.     /*
  271.      * If we're aborting before any files transferred or after an
  272.      * abort or quit, just close down the whole connection.
  273.      */
  274.     if (ftpc->state < FTPS_READY || ftpc->state > FTPS_EOF) {
  275.     ftpDone(ftpc);
  276.     return;
  277.     }
  278.     /*
  279.      * Close the dataconn so we don't get swamped in select() while
  280.      * waiting for the response to the abort on the ctrlconn.
  281.      */
  282.     if (ftpc->data != -1) {
  283.     UnregisterFtpFd(ftpc,ftpc->data);
  284.     }
  285.     /* Then send the abort sequence */
  286.     ftpSendAbort(ftpc);
  287.     DEBUG0("ftpAbort: done\n");
  288. }
  289.  
  290. /*    -    -    -    -    -    -    -    -    */
  291. /* Initialization: */
  292. /*
  293.  * This function is called from ftpNewContext() to initialize the host
  294.  * address information in the FTPC.
  295.  */
  296. static int
  297. ftpGetHostAndPort(ftpc)
  298. FtpContext *ftpc;
  299. {
  300.     struct servent *serv;
  301.     struct hostent *host;
  302.     unsigned long hostaddr;
  303.     char msg[256];
  304.     int i;
  305.  
  306.     DEBUG2("ftpGetHostAndPort: ftpc=0x%x: \"%s\"\n",ftpc,ftpc->hostname);
  307.     /*
  308.      * Get ftp port
  309.      */
  310.     DEBUG0("ftpGetHostAndPort: getting ftp service port\n");
  311.     serv = getservbyname("ftp","tcp");
  312.     /* UCX needs 0 or -1 */
  313.     if (serv == (struct servent *)0 || serv == (struct servent *)-1) {
  314.     alert0("Can't find service 'ftp/tcp' in list of services -- using default port");
  315.     ftpc->servport = htons((unsigned short)IPPORT_FTP);
  316.     } else {
  317.     ftpc->servport = (unsigned short)serv->s_port;
  318.     }
  319.     DEBUG1("ftpGetHostAndPort: ftp service port is %d\n",ftpc->servport);
  320.     /*
  321.      * Get host address
  322.      */
  323.     sprintf(msg,"Getting address for host \"%.200s\"",ftpc->hostname);
  324.     status0(msg);
  325.     if ((host=gethostbyname(ftpc->hostname)) == NULL) {
  326.     DEBUG0("ftpGetHostAndPort: gethostbyname failed, trying inet_addr()\n");
  327.     /*
  328.      * If gethostbyname fails, then maybe we've been given an IP
  329.      * address directly. Let's see.
  330.      */
  331.     hostaddr = inet_addr(ftpc->hostname);
  332.     if (hostaddr == (unsigned long)-1) {
  333.         /*
  334.          * Nope - complete failure.
  335.          */
  336.         sprintf(msg,"Can't find address of host \"%.200s\"",
  337.             ftpc->hostname);
  338.         alert0(msg);
  339.         return(-1);
  340.     } else {
  341.         /*
  342.          * inet_addr succeeded, so we make a dummy array of hostaddrs.
  343.          */
  344.         ftpc->h_addr_list = (char **)calloc(2,sizeof(char *));
  345.         bcopy((char *)&hostaddr,ftpc->h_addr_list[0],sizeof(char *));
  346.         hostaddr = (unsigned long)0;
  347.         bcopy((char *)&hostaddr,ftpc->h_addr_list[1],sizeof(char *));
  348.         ftpc->this_addr = ftpc->h_addr_list-1; /* preincrement later */
  349.         DEBUG1("ftpGetHostAndPort: inet_addr returned %s\n",
  350.            inet_ntoa(*(struct in_addr*)(ftpc->h_addr_list[0])));
  351.         return(0);
  352.     }
  353.     } else {
  354.     /*
  355.      * If gethostbyname succeeeds, it fills in a list of addresses.
  356.      * We'll copy them into the ftpc.
  357.      */
  358.     for (i=0; host->h_addr_list[i]; i++)
  359.         /*EMPTY*/;
  360.     DEBUG1("ftpGetHostAndPort: gethostbynname returned %d addr(s)\n",i);
  361.     ftpc->h_addr_list = (char **)calloc(i+1,sizeof(char *));
  362.     for (i=0; host->h_addr_list[i]; i++) {
  363.         bcopy(host->h_addr_list[i],
  364.           (char *)(ftpc->h_addr_list+i),sizeof(char *));
  365.         DEBUG2("ftpGetHostAndPort: h_addr_list[%d] = %s\n",i,
  366.            inet_ntoa(*(struct in_addr*)(ftpc->h_addr_list+i)));
  367.     }
  368.     bzero((char *)(ftpc->h_addr_list+i),sizeof(char *));
  369.     ftpc->this_addr = ftpc->h_addr_list-1;    /* preincrement later */
  370.     return(0);
  371.     }
  372. }
  373.  
  374. /*    -    -    -    -    -    -    -    -    */
  375. /* Connection establishment: */
  376. /*
  377.  * This function goes through the list of addresses trying to connect to
  378.  * the server. It is initially called from ftpNewContext(), and subsequently
  379.  * whenever the ctrl socket it ready for writing (indicating connect()
  380.  * completed, possibly with an error).
  381.  */
  382. static void
  383. ftpConnectCallback(ftpc)
  384. FtpContext *ftpc;
  385. {
  386.     int retcode;
  387.     char msg[256];
  388.  
  389.     DEBUG2("ftpConnectCallback: ftpc=0x%x, state=%s\n",
  390.        ftpc,ftpstatestr(ftpc->state));
  391.   redo:
  392.     switch (ftpc->state) {
  393.       case FTPS_OPEN:
  394.     ftpc->this_addr += 1;            /* preincrement */
  395.     DEBUG1("ftpConnectCallback: OPEN: %s\n",
  396.            inet_ntoa(*(struct in_addr*)(ftpc->this_addr)));
  397.     if (*(ftpc->this_addr)) {        /* Not at last host */
  398.         if (ftpGetSocket(ftpc) < 0) {
  399.         ftpDone(ftpc);
  400.         } else {
  401.         /* Arrange to get called back if connect() would block */
  402.         RegisterFtpFd(ftpc,ftpc->ctrl,O_RDWR,ftpConnectCallback);
  403.         ftpc->state = FTPS_CONNECT;
  404.         goto redo;
  405.         }
  406.     } else {                /* All hosts failed */
  407.         sprintf(msg,"Couldn't connect to host \"%s\"",ftpc->hostname);
  408.         alert0(msg);
  409.         ftpDone(ftpc);
  410.     }
  411.     break;
  412.       case FTPS_CONNECT:
  413.     /* We previously registered either above or below */
  414.     UnregisterFtpFd(ftpc,ftpc->ctrl);
  415.     retcode = ftpConnect(ftpc);
  416.     if (retcode < 0) {
  417.         DEBUG1("ftpConnectCallback: ftpConnect failed, closing ctrl %d\n",
  418.            ftpc->ctrl);
  419.         close(ftpc->ctrl);
  420.         ftpc->ctrl = -1;
  421.         ftpc->state = FTPS_OPEN;        /* try next one */
  422.         goto redo;
  423.     } else if (retcode == 0) {
  424.         DEBUG0("ftpConnectCallback: ftpConnect would block\n");
  425.         /* Arrange to get called back when connect() won't block */
  426.         RegisterFtpFd(ftpc,ftpc->ctrl,O_RDWR,ftpConnectCallback);
  427.         ftpc->state = FTPS_CONNECT;        /* try again when ready */
  428.     } else {
  429.         DEBUG0("ftpConnectCallback: ftpConnect ok\n");
  430.         ftpInitReply(ftpc);            /* ok... */
  431.         ftpc->state = FTPS_CONNECTED;    /* move on */
  432.     }
  433.     break;
  434.       default:
  435.     fprintf(stderr,"ftpConnectCallback: unknown state %d\n",ftpc->state);
  436.     abort();
  437.     }
  438.     DEBUG0("ftpConnectCallback: done\n");
  439. }
  440.         
  441. /*    -    -    -    -    -    -    -    -    */
  442. /* Functions called by ftpConnectCallback(): */
  443. /*
  444.  * This function opens a new socket for the ctrl connection, and sets
  445.  * it up for non-blocking IO. Everything is setup for later connect().
  446.  */
  447. static int
  448. ftpGetSocket(ftpc)
  449. FtpContext *ftpc;
  450. {
  451.     DEBUG1("ftpGetSocket: addr=%s\n",
  452.        inet_ntoa(*(struct in_addr*)(ftpc->this_addr)));
  453.     /* Get a socket */
  454.     if ((ftpc->ctrl=socket(AF_INET,SOCK_STREAM,0)) < 0) {
  455.     sysError("socket(ftpGetSocket)");
  456.     return(-1);
  457.     }
  458.     DEBUG1("ftpGetSocket: socket() returned %d\n",ftpc->ctrl);
  459.     /*
  460.      * According to jbh@moses.aii.COM, we can't do this until we connect
  461.      * on (at least) ISC variants of SYS5. I have not had problems with
  462.      * other SYS5 systems, including HPUX. When ISC is defined, we do it
  463.      * in ftpConnect(), although we may then block in connect().
  464.      */
  465. #ifndef ISC
  466.     /* Setup socket for async i/o */
  467.     MAKE_NONBLOCKING(ftpc->ctrl);
  468. #endif
  469.     /* Set up the address spec. */
  470.     bzero((char *)&(ftpc->saddr),sizeof(struct sockaddr_in));
  471.     ftpc->saddr.sin_family = AF_INET;
  472.     ftpc->saddr.sin_port = ftpc->servport;
  473.     ftpc->saddr.sin_addr = *((struct in_addr *)(ftpc->this_addr));
  474.     /* Ready to connect() */
  475.     DEBUG0("ftpGetSocket: done\n");
  476.     return(0);
  477. }
  478.  
  479. /*
  480.  * Calls connect(). Returns -1 if this host is botched, 0 if connect()
  481.  * would have blocked, or 1 if we should proceed. 
  482.  */
  483. static int
  484. ftpConnect(ftpc)
  485. FtpContext *ftpc;
  486. {
  487.     int retcode,addrlen;
  488.     char msg[256];
  489.  
  490.     sprintf(msg,"Connecting to %.200s (%s)",
  491.         ftpc->hostname,inet_ntoa(*(struct in_addr*)(ftpc->this_addr)));
  492.     status0(msg);
  493.     DEBUG0("ftpConnect: calling connect()...\n");
  494.     retcode = connect(ftpc->ctrl,(struct sockaddr *)&(ftpc->saddr),
  495.               sizeof(struct sockaddr_in));
  496.     if (retcode < 0 && errno == EINPROGRESS) {
  497.     DEBUG0("ftpConnect: connect() EINPROGRESS\n");
  498.     return(0);
  499.     } else if (retcode < 0 && errno != EISCONN) {
  500. #ifdef DEBUG
  501.     perror("ftpConnect: connect()");
  502. #endif
  503.     return(-1);
  504.     }
  505. #ifdef DEBUG
  506.       else if (retcode < 0 && errno == EISCONN)
  507.       fprintf(stderr,"ftpConnect: connect() EISCONN\n");
  508.       else
  509.       fprintf(stderr,"ftpConnect: connect() returned ok\n");
  510. #endif
  511.      /* According to jbh@moses.aii.COM, see comments in ftpGetSocket(). */
  512. #ifdef ISC
  513.     MAKE_NONBLOCKING(ftpc->ctrl); /* Setup socket for async i/o */
  514. #endif
  515.     /* Get name (address) of socket */
  516.     addrlen = sizeof(struct sockaddr_in);
  517.     retcode =
  518.     getsockname(ftpc->ctrl,(struct sockaddr *)&(ftpc->saddr),&addrlen);
  519.     if (retcode < 0) {
  520.     sysError("getsockname(ftpConnect)");
  521.     return(-1);
  522.     }
  523.     DEBUG1("ftpConnect: getsockname() returned %s\n",
  524.        inet_ntoa(ftpc->saddr.sin_addr));
  525.     return(1);
  526. }
  527.  
  528. /*    -    -    -    -    -    -    -    -    */
  529. /* Reading server replies: */
  530. /*
  531.  * This function must be called to initialize the reply in the FTPC and
  532.  * to ensure that the appropriate callback is registered to read it.
  533.  */
  534. static void
  535. ftpInitReply(ftpc)
  536. FtpContext *ftpc;
  537. {
  538.     DEBUG1("ftpInitReply: ftpc=0x%lx\n",ftpc);
  539.     ftpc->retcode = 0;
  540.     ftpc->reply_len = 0;
  541.     *(ftpc->reply) = '\0';
  542.     ftpc->reply_state = FTPS_REPLY_CODE;
  543.     /* We're expecting a reply, so register ctrl for reading only */
  544.     RegisterFtpFd(ftpc,ftpc->ctrl,O_RDONLY,ftpReplyCallback);
  545.     DEBUG0("ftpInitReply: done\n");
  546. }
  547.  
  548. /*
  549.  * This function is the low-level reply parser. It reads and
  550.  * parses reply messages from the server, handles TELNET commands,
  551.  * and translate codes in the reply prefix into integers. It stores the
  552.  * reply code in ftpc->retcode and puts the text of the reply in ftpc->reply.
  553.  * It is called whenever the ctrlconn is ready for reading and we are
  554.  * expecting a reply (ie., after ftpInitReply()). Once the reply is
  555.  * complete, we call ftpProcessReply().
  556.  */
  557. static void
  558. ftpReplyCallback(ftpc)
  559. FtpContext *ftpc;
  560. {
  561.     int n;
  562.     unsigned char c,cc;
  563.  
  564.     for (;;) {
  565. /*
  566.     DEBUG2("ftpReplyCallback: ftpc=0x%x, reply_state=%s\n",
  567.            ftpc,ftpstatestr(ftpc->reply_state));
  568. */
  569.     n = read(ftpc->ctrl,&c,1);        /* Read a byte */
  570.     if (n < 0 && errno != ITWOULDBLOCK) {        /* Error */
  571.         sysError("read(ftpReplyCallback)");
  572.         DEBUG0("ftpReplyCallback: error reading ctrlconn\n");
  573.         ftpDone(ftpc);
  574.         return;
  575.     } else if (n < 0 && errno == ITWOULDBLOCK) {    /* Would block */
  576.         /*DEBUG0("ftpReplyCallback: ctrlconn would block\n");*/
  577.         return;
  578.     } else if (n == 0) {            /* EOF */
  579.         *(ftpc->reply+ftpc->reply_len) = '\0';
  580.         /* Could check against current retcode, if any... */
  581.         if (ftpc->state == FTPS_QUIT)
  582.         ftpc->retcode = FTP_SERVICE_CLOSING;
  583.         else
  584.         ftpc->retcode = FTP_SERVICE_UNAVAILABLE;
  585.         DEBUG1("ftpReplyCallback: EOF: retcode = %d\n",ftpc->retcode);
  586.         /* Reply is done, go process it */
  587.         break;
  588.     }
  589.     /*
  590.      * Otherwise, we got something, process it.
  591.      */
  592.     if (c == IAC) {            /* Telnet IAC */
  593.         ftpc->saved_state = ftpc->reply_state;
  594.         ftpc->reply_state = FTPS_REPLY_IAC1;
  595.         DEBUG1("ftpReplyCallback: IAC (saved_state=%d)\n",ftpc->saved_state);
  596.         continue;
  597.     } else if (c == '\r') {        /* Skip <CR>, is this ok? */
  598.         continue;
  599.     }
  600.     /*
  601.      * We got something that's not IAC, process it.
  602.      */
  603.     /*DEBUG2("ftpReplyCallback: `%c' (%d)\n",c,c);*/
  604.     switch (ftpc->reply_state) {
  605.       case FTPS_REPLY_IAC1:
  606.         DEBUG1("ftpReplyCallback: IAC %c",c);
  607.         ftpc->reply_state = FTPS_REPLY_IAC2;
  608.         break;
  609.       case FTPS_REPLY_IAC2:
  610.         switch (c) {
  611.           case WILL:
  612.           case WONT: c = DONT;
  613.              break;
  614.           case DO:
  615.           case DONT: c = WONT;
  616.              break;
  617.           default:
  618.         DEBUG0(" (ignored)\n");
  619.         continue;
  620.         }
  621.         DEBUG1(", reply %c\n",c);
  622.         cc = IAC;
  623.         write(ftpc->ctrl,&cc,1);
  624.         write(ftpc->ctrl,&c,1);
  625.         ftpc->reply_state = FTPS_REPLY_IAC3;
  626.         break;
  627.       case FTPS_REPLY_IAC3:
  628.         write(ftpc->ctrl,&c,1);
  629.         ftpc->reply_state = ftpc->saved_state;
  630.         DEBUG1("ftpReplyCallback: IAC done, restored state = %d\n",
  631.            ftpc->reply_state);
  632.         break;
  633.       case FTPS_REPLY_CODE:
  634.         if (c < '0' || c > '9') {
  635.         *(ftpc->reply+ftpc->reply_len) = '\0';
  636.         DEBUG1("ftpReplyCallback: CODE: retcode = %d\n",ftpc->retcode);
  637.         goto done;
  638.         }
  639.         *(ftpc->reply+ftpc->reply_len++) = c;
  640.         ftpc->retcode = ftpc->retcode * 10 + c - '0';
  641.         /*DEBUG1("ftpReplyCallback: retcode now %d\n",ftpc->retcode);*/
  642.         if (ftpc->retcode >= 100)
  643.         ftpc->reply_state = FTPS_REPLY_CONT;
  644.         break;
  645.       case FTPS_REPLY_CONT:
  646.         /* we reach here after we finished reading the code or when we
  647.          * struck a line beginning with at least three digits, check if
  648.          * this is the last line of the reply
  649.          */
  650.         *(ftpc->reply+ftpc->reply_len++) = c;
  651.         if (c == '-') {
  652. /*
  653.         DEBUG0("ftpReplyCallback: continuation\n");
  654. */
  655.         ftpc->reply_state = FTPS_REPLY_MORE;
  656.         } else {
  657. /*
  658.         DEBUG0("ftpReplyCallback: final line\n");
  659. */
  660.         ftpc->reply_state = FTPS_REPLY_LAST;
  661.         }
  662.         break;
  663.       case FTPS_REPLY_LAST:
  664.         if (c == '\n') {
  665.         *(ftpc->reply+ftpc->reply_len) = '\0';
  666. /*
  667.         DEBUG1("ftpReplyCallback: LAST: retcode = %d\n",ftpc->retcode);
  668. */
  669.         goto done;
  670.         } else {
  671.         *(ftpc->reply+ftpc->reply_len++) = c;
  672. /*
  673.         DEBUG3("ftpReplyCallback: LAST: %03d: \"%.*s\"\n",
  674.              ftpc->reply_len,ftpc->reply_len,ftpc->reply);
  675. */
  676.         }
  677.         break;
  678.       case FTPS_REPLY_MORE:
  679.         *(ftpc->reply+ftpc->reply_len++) = c;
  680. /*
  681.         DEBUG3("ftpReplyCallback: MORE: %03d: \"%.*s\"\n",
  682.            ftpc->reply_len,ftpc->reply_len,ftpc->reply);
  683. */
  684.         if (c == '\n') {
  685.         ftpc->tmpcode = 0;
  686.         ftpc->reply_state = FTPS_REPLY_CHCK;
  687.         }
  688.         break;
  689.       case FTPS_REPLY_CHCK:
  690.         if (c < '0' || c > '9') {
  691.         *(ftpc->reply+ftpc->reply_len++) = c;
  692. /*
  693.         DEBUG3("ftpReplyCallback: CHCK: %03d: \"%.*s\"\n",
  694.                ftpc->reply_len,ftpc->reply_len,ftpc->reply);
  695. */
  696.         ftpc->reply_state = FTPS_REPLY_MORE;
  697.         } else {
  698.         ftpc->tmpcode = ftpc->tmpcode * 10 + c - '0';
  699.         if (ftpc->tmpcode >= 100) {
  700.             if (ftpc->tmpcode != ftpc->retcode)
  701.             ftpc->reply_state = FTPS_REPLY_MORE;
  702.             else
  703.             ftpc->reply_state = FTPS_REPLY_CONT;
  704.         }
  705.         }
  706.         break;
  707.       default:
  708.         fprintf(stderr,"ftpReplyCallback: unknown reply_state %d\n",
  709.             ftpc->reply_state);
  710.         abort();
  711.     }
  712.     }
  713.     /* if we get here, the reply is complete */
  714.   done:
  715.     UnregisterFtpFd(ftpc,ftpc->ctrl);
  716.     if (ftpc->trace) {
  717.     (*(ftpc->trace))(ftpc,0,ftpc->reply);
  718.     }
  719.     ftpProcessReply(ftpc);
  720. }
  721.  
  722. /*    -    -    -    -    -    -    -    -    */
  723. /*
  724.  * This function implements the high-level FTP protocol using the state
  725.  * field of the FTPC. It is called from ftpReplyCallback() once we've read
  726.  * a reply and need to process it.
  727.  */
  728. static void
  729. ftpProcessReply(ftpc)
  730. FtpContext *ftpc;
  731. {
  732.     char cmd[256];
  733.  
  734.     DEBUG3("ftpProcessReply: ftpc=0x%x, state=%s, retcode=%d\n",
  735.        ftpc,ftpstatestr(ftpc->state),ftpc->retcode);
  736.   redo:
  737.     switch (ftpc->state) {
  738.       case FTPS_CONNECTED:
  739.     if (ftpc->retcode == FTP_SERVICE_RDY_TIME) {    /* delay NNN minutes */
  740.         DEBUG0("ftpProcessReply: server not ready\n");
  741.         ftpAlert(ftpc);
  742.     } else if (ftpc->retcode == FTP_SERVICE_RDY_USER) {/* ready for USER */
  743.         sprintf(cmd,"USER %s",ftpc->user);
  744.         status0(cmd);
  745.         ftpc->state = FTPS_USER;
  746.         ftpSendCmd(ftpc,cmd);
  747.     } else {                /* FTP_SERVICE_UNAVAILABLE */
  748.         ftpAlert(ftpc);
  749.         ftpDone(ftpc);
  750.     }
  751.     break;
  752.       case FTPS_USER:
  753.     if (ftpc->retcode == FTP_LOGIN_OK) {    /* USER ok, no PASS needed */
  754.         ftpc->retcode = FTP_FILE_ACTION_OK;
  755.         ftpc->state = FTPS_CWD;
  756.         goto redo;
  757.     } else if (ftpc->retcode == FTP_LOGIN_NEED_PASSWD) {    /* USER ok   */
  758.         sprintf(cmd,"PASS %s",ftpc->pass);            /* need PASS */
  759.         status0(cmd);
  760.         ftpc->state = FTPS_PASS;
  761.         ftpSendCmd(ftpc,cmd);
  762.     } else {                /* ACCT needed or error */
  763.         ftpHandleErrorThenQuit(ftpc);
  764.     }
  765.     break;
  766.       case FTPS_PASS:
  767.     if (ftpc->retcode == FTP_LOGIN_OK) {    /* PASS ok, ready to go */
  768.         ftpc->retcode = FTP_FILE_ACTION_OK;
  769.         ftpc->state = FTPS_CWD;
  770.         goto redo;
  771.     } else {                /* ACCT needed or error */
  772.         ftpHandleErrorThenQuit(ftpc);
  773.     }
  774.     break;
  775.       case FTPS_CWD:
  776.     /* can come here direct from USER or PASS also... */
  777.     if (ftpc->retcode == FTP_FILE_ACTION_OK ||
  778.         ftpc->retcode == FTP_COMMAND_OK) {    /* last CWD ok */
  779.         if (ftpc->wd == NULL || *(ftpc->wd) == '\0') { /* CWD done */
  780.         ftpc->state = FTPS_TYPE;
  781.         ftpSendType(ftpc);
  782.         } else {                /* send next part of CWD */
  783.         ftpc->state = FTPS_CWD;
  784.         ftpSendNextCwd(ftpc);
  785.         }
  786.     } else {                /* last CWD failed */
  787.         ftpHandleErrorThenQuit(ftpc);
  788.     }
  789.     break;
  790.       case FTPS_TYPE:
  791.     if (ftpc->retcode == FTP_COMMAND_OK) {    /* TYPE ok */
  792.         ftpc->retcode = FTP_FILE_ACTION_OK;
  793.         ftpc->state = FTPS_READY;
  794.         goto redo;
  795.     } else {                /* TYPE failed */
  796.         ftpHandleErrorThenQuit(ftpc);
  797.     }
  798.     break;
  799.       case FTPS_READY:
  800.     /* can get here from TYPE or EOF */
  801.     ftpc->this_file += 1;
  802.     if (ftpc->this_file < ftpc->num_files) {  /* files left to transfer */
  803.         sprintf(cmd,"File %s?",ftpc->files[ftpc->this_file]);
  804.         status0(cmd);
  805.         if (ftpc->prompt && !ftpPrompt(ftpc)) {
  806.         goto redo;
  807.         }
  808.         if (ftpSendPort(ftpc) == 0) {    /*   PORT ok locally */
  809.         ftpc->state = FTPS_PORT;
  810.         } else {                /*   PORT failed locally */
  811.         ftpc->state = FTPS_QUIT;    /*     bag the whole thing */
  812.         ftpSendCmd(ftpc,"QUIT");
  813.         }
  814.     } else {                /* no files left to transfer */
  815.         DEBUG0("ftpProcessReply: no more files\n");
  816.         ftpc->state = FTPS_QUIT;
  817.         ftpSendCmd(ftpc,"QUIT");
  818.     }
  819.     break;
  820.       case FTPS_PORT:
  821.     if (ftpc->retcode == FTP_COMMAND_OK) {    /* PORT command ok */
  822.         ftpc->state = FTPS_GETPUT;
  823.         ftpSendGetPut(ftpc);
  824.     } else {                /* PORT failed */
  825.         if (ftpc->port >= 0) {
  826.         DEBUG1("ftpProcessReply: closing port %d\n",ftpc->port);
  827.         close(ftpc->port);
  828.         ftpc->port = -1;
  829.         }
  830.         ftpHandleErrorThenQuit(ftpc);
  831.     }
  832.     break;
  833.       case FTPS_GETPUT:
  834.     if (FTP_REPLY_PRELIM(ftpc->retcode)) { /* dataconn ready */
  835.         status0(ftpc->reply+4);
  836.         if (sscanf(ftpc->reply,"%*[^(](%d bytes)",&(ftpc->this_size)) != 1)
  837.         ftpc->this_size = 0;
  838.         if (ftpAcceptDataConn(ftpc) < 0) {        /*  local failure */
  839.         if (ftpc->port != -1) {            /*    give up */
  840.             DEBUG1("ftpProcessReply: closing port %d\n",ftpc->port);
  841.             close(ftpc->port);
  842.             ftpc->port = -1;
  843.         }
  844.         ftpc->state = FTPS_QUIT;
  845.         ftpSendCmd(ftpc,"QUIT");
  846.         } else if (ftpSetupLocalData(ftpc) < 0) {    /*  local failure */
  847.         UnregisterFtpFd(ftpc,ftpc->data);    /*    don't give up */
  848.         if (ftpc->data != -1) {
  849.             DEBUG1("ftpProcessReply: closing data %d\n",ftpc->data);
  850.             close(ftpc->data);
  851.             ftpc->data = -1;
  852.         }
  853.         ftpc->state = FTPS_READY;        /*    do next file */
  854.         goto redo;
  855.         } else {                /* all ok locally */
  856.         ftpc->state = FTPS_TRANSFER;
  857.         }
  858.     } else if (ftpc->retcode == FTP_FILE_UNAVAILABLE ||    /* datacon */
  859.            ftpc->retcode == FTP_ACTION_NOT_TAKEN) {    /* failed  */
  860.         if (ftpc->port != -1) {                /*  minor  */
  861.         DEBUG1("ftpProcessReply: closing port %d\n",ftpc->port);
  862.         close(ftpc->port);                /*  error */
  863.         ftpc->port = -1;
  864.         }
  865.         ftpAlert(ftpc);                /* error msg */
  866.         ftpc->state = FTPS_READY;        /* next file */
  867.         goto redo;
  868.     } else {
  869.         ftpHandleErrorThenQuit(ftpc);    /* real error */
  870.     }
  871.     break;
  872.       case FTPS_TRANSFER:
  873.     /* Shouldn't get here since dataconn is separately managed */
  874.     DEBUG0("ftpProcessReply: called in state TRANSFER!\n");
  875.     break;
  876.       case FTPS_EOF:
  877.     /* Called when the dataconn is closed, for whatever reason */
  878.     if (!FTP_REPLY_OK(ftpc->retcode)) {    /* dataconn closed error */
  879.         ftpAlert(ftpc);
  880.     }
  881.     ftpc->state = FTPS_READY;
  882.     goto redo;
  883.       case FTPS_QUIT:
  884.     if (ftpc->retcode != FTP_SERVICE_CLOSING) {    /* error */
  885.         ftpAlert(ftpc);
  886.     }
  887.     ftpDone(ftpc);                /* close, deallocate */
  888.     break;
  889.       case FTPS_ABORT:
  890.     if (ftpc->retcode == 552) {        /* NIC-style abort */
  891.         ftpc->state = FTPS_ABORT;        /* get another reply */
  892.     } else {
  893.         if (ftpc->retcode != FTP_DATA_CLOSE_ABORT) { /* 426      */
  894.         ftpAlert(ftpc);                 /* expected */
  895.         }
  896.         ftpc->state = FTPS_ABORT2;        /* get real reply */
  897.         ftpInitReply(ftpc);
  898.     }
  899.     break;
  900.       case FTPS_ABORT2:
  901.     if (!FTP_REPLY_OK(ftpc->retcode)) {    /* abort error */
  902.         ftpAlert(ftpc);
  903.     }
  904.     ftpCleanupDataConn(ftpc);
  905.     ftpc->state = FTPS_READY;
  906.     goto redo;
  907.       default:
  908.     fprintf(stderr,"ftpProcessReply: unknown state: %d\n",ftpc->state);
  909.     abort();
  910.     }
  911.     DEBUG0("ftpProcessReply: done\n");
  912. }
  913.  
  914. /*
  915.  * This function just puts up an FTP error message.
  916.  */
  917. static void
  918. ftpAlert(ftpc)
  919. FtpContext *ftpc;
  920. {
  921.     char buf[256],*msg;
  922.     int len;
  923.  
  924.     msg = ftpc->reply+4;        /* skip code */
  925.     len = strlen(msg);
  926.     if (len < 230) {
  927.     sprintf(buf,"FTP Error %d:\n %s",ftpc->retcode,msg);
  928.     } else {
  929.     sprintf(buf,"FTP Error %d: ...\n %.230s",ftpc->retcode,msg+len-230);
  930.     }
  931.     alert0(buf);
  932. }
  933.  
  934. /*
  935.  * This prints an error message, then sends the QUIT command (or
  936.  * calls ftpDone() if the server shut down).
  937.  */
  938. static void
  939. ftpHandleErrorThenQuit(ftpc)
  940. FtpContext *ftpc;
  941. {
  942.     ftpAlert(ftpc);
  943.     /* Don't bother with QUIT if remote host closing down */
  944.     if (ftpc->retcode == FTP_SERVICE_UNAVAILABLE) {
  945.     ftpDone(ftpc);
  946.     } else {
  947.     status0("Quitting...");
  948.     ftpc->state = FTPS_QUIT;
  949.     ftpSendCmd(ftpc,"QUIT");
  950.     }
  951. }
  952.  
  953. /*
  954.  * This calls the done function (or ftpFreeContext() if none is defined
  955.  * for FTPC. All connections should pass through here to be cleaned up
  956.  * regardless of how they got here.
  957.  */
  958. static void
  959. ftpDone(ftpc)
  960. FtpContext *ftpc;
  961. {
  962.     DEBUG1("ftpDone: ftpc=0x%x\n",ftpc);
  963.     if (ftpc->done != NULL) {
  964.     (*(ftpc->done))(ftpc);
  965.     } else {
  966.     ftpFreeContext(ftpc);
  967.     }
  968.     DEBUG0("ftpDone: done\n");
  969. }
  970.  
  971. /*    -    -    -    -    -    -    -    -    */
  972. /* Functions called by ftpProcessReply(): */
  973.  
  974. static void
  975. ftpSendType(ftpc)
  976. FtpContext *ftpc;
  977. {
  978.     char cmd[16];
  979.  
  980.     DEBUG2("ftpSendType: ftpc=0x%x, type=%d\n",ftpc,ftpc->type);
  981.     ftpc->state = FTPS_TYPE;
  982.     switch (ftpc->type) {
  983.       case TYPE_A:
  984.      sprintf(cmd,"TYPE A");
  985.     break;
  986.       case TYPE_E:
  987.     sprintf(cmd,"TYPE E");
  988.     break;
  989.       case TYPE_I:
  990.     sprintf(cmd,"TYPE I");
  991.     break;
  992.       default:
  993.     sprintf(cmd,"TYPE L %d",(char *)ftpc->type);
  994.     }
  995.     status0(cmd);
  996.     ftpSendCmd(ftpc,cmd);
  997.     DEBUG0("ftpSendType: done\n");
  998. }
  999.  
  1000. static void
  1001. ftpSendNextCwd(ftpc)
  1002. FtpContext *ftpc;
  1003. {
  1004.     char *slash,cmd[256];
  1005.  
  1006.     DEBUG2("ftpSendNextCwd: ftpc=0x%x, wd=\"%s\"\n",ftpc,ftpc->wd);
  1007.     if (*(ftpc->wd) == '/') {    /* Leading slash treated specially... */
  1008.     ftpc->state = FTPS_CWD;
  1009.     ftpSendCmd(ftpc,"CWD /");
  1010.     ftpc->wd += 1;
  1011.     } else {            /* Normal case */
  1012.     if ((slash=index(ftpc->wd,'/')) != NULL) {
  1013.         *slash = '\0';
  1014.     }
  1015.     sprintf(cmd,"CWD %s",ftpc->wd);
  1016.     status0(cmd);
  1017.     ftpc->state = FTPS_CWD;
  1018.     ftpSendCmd(ftpc,cmd);
  1019.     if (slash) {
  1020.         ftpc->wd = slash+1;
  1021.     } else {
  1022.         /* set wd to end of string */
  1023.         while (*(ftpc->wd) != '\0')
  1024.         ftpc->wd += 1;
  1025.     }
  1026.     }
  1027.     DEBUG0("ftpSendNextCwd: done\n");
  1028. }
  1029.  
  1030. /*
  1031.  * Performs the PORT command. The new port is saved in f->port.
  1032.  * Returns < 0 if some local error, otherwise 0 if we got to sending
  1033.  * the command.
  1034.  */
  1035. static int
  1036. ftpSendPort(ftpc)
  1037. FtpContext *ftpc;
  1038. {
  1039.     char cmd[64];
  1040.     struct sockaddr_in addr;
  1041.     int addrlen;
  1042.  
  1043.     DEBUG1("ftpSendPort: ftpc=0x%lx\n",ftpc);
  1044.     if ((ftpc->port=socket(AF_INET,SOCK_STREAM,0)) < 0) {
  1045.     sysError("socket(ftpSendPort)");
  1046.     DEBUG0("ftpSendPort: returning -1\n");
  1047.     return(-1);
  1048.     }
  1049.     DEBUG1("ftpSendPort: socket() returned %d\n",ftpc->port);
  1050.     addr = ftpc->saddr;
  1051.     addr.sin_port = 0;
  1052.     if (bind(ftpc->port,(struct sockaddr *)&addr,
  1053.          sizeof(struct sockaddr_in)) < 0) {
  1054.     sysError("bind(ftpSendPort)");
  1055.     DEBUG1("ftpSendPort: closing port %d\n",ftpc->port);
  1056.     close(ftpc->port);
  1057.     DEBUG0("ftpSendPort: returning -1\n");
  1058.     return(-1);
  1059.     }
  1060.     DEBUG0("ftpSendPort: bind() succeeded\n");
  1061.     if (listen(ftpc->port,1) < 0) {
  1062.     sysError("listen(ftpSendPort)");
  1063.     DEBUG1("ftpSendPort: closing port %d\n",ftpc->port);
  1064.     close(ftpc->port);
  1065.     DEBUG0("ftpSendPort: returning -1\n");
  1066.     return(-1);
  1067.     }
  1068.     DEBUG0("ftpSendPort: listen() succeeded\n");
  1069.     addrlen = sizeof(struct sockaddr_in);
  1070.     if (getsockname(ftpc->port,(struct sockaddr *)&addr,&addrlen) < 0) {
  1071.     sysError("getsockname(ftpSendPort)");
  1072.     DEBUG1("ftpSendPort: closing port %d\n",ftpc->port);
  1073.     close(ftpc->port);
  1074.     DEBUG0("ftpSendPort: returning -1\n");
  1075.     return(-1);
  1076.     }
  1077.     DEBUG2("ftpSendPort: PORT address: %s; port: %d\n",
  1078.        inet_ntoa(addr.sin_addr),addr.sin_port);
  1079.     sprintf(cmd,"PORT %d,%d,%d,%d,%d,%d",
  1080.         (int)((unsigned char *)&addr.sin_addr.s_addr)[0],
  1081.         (int)((unsigned char *)&addr.sin_addr.s_addr)[1],
  1082.         (int)((unsigned char *)&addr.sin_addr.s_addr)[2],
  1083.         (int)((unsigned char *)&addr.sin_addr.s_addr)[3],
  1084.         (int)((unsigned char *)&addr.sin_port)[0],
  1085.         (int)((unsigned char *)&addr.sin_port)[1]);
  1086.     status0(cmd);
  1087.     ftpc->state = FTPS_PORT;
  1088.     ftpSendCmd(ftpc,cmd);
  1089.     DEBUG0("ftpSendPort: returning 0\n");
  1090.     return(0);
  1091. }
  1092.  
  1093.  
  1094. /*
  1095.  * After PORT is ok, send GET or PUT to open the dataconn.
  1096.  */
  1097. static void
  1098. ftpSendGetPut(ftpc)
  1099. FtpContext *ftpc;
  1100. {
  1101.     char cmd[MAXPATHLEN];
  1102.  
  1103.     DEBUG2("ftpSendGetPut: ftpc=0x%x, fcmd=%d\n",ftpc,ftpc->filecmd);
  1104.     ftpc->state = FTPS_GETPUT;
  1105.     switch (ftpc->filecmd) {
  1106.       case FTP_GET:
  1107.     sprintf(cmd,"RETR %s",ftpc->files[ftpc->this_file]);
  1108.     break;
  1109.       case FTP_PUT:
  1110.     sprintf(cmd,"STOR %s",ftpc->files[ftpc->this_file]);
  1111.     break;
  1112.     }
  1113.     status0(cmd);
  1114.     ftpSendCmd(ftpc,cmd);
  1115.     DEBUG0("ftpSendGetPut: done\n");
  1116. }
  1117.  
  1118. /*
  1119.  * After GET/PUT, accepts the connection from the PORT and closes the
  1120.  * the PORT. Returns the dataconn fd (also in ftpc->data).
  1121.  */
  1122. static int
  1123. ftpAcceptDataConn(ftpc)
  1124. FtpContext *ftpc;
  1125. {
  1126.     int datacon,addrlen;
  1127.     struct sockaddr_in addr;
  1128.  
  1129.     DEBUG1("ftpAcceptDataConn: fcmd %d successful\n",ftpc->filecmd);
  1130.     addrlen = sizeof(struct sockaddr_in);
  1131.     if ((datacon=accept(ftpc->port,(struct sockaddr *)&addr,&addrlen)) < 0) {
  1132.     sysError("accept(ftpAcceptDataConn)");
  1133.     DEBUG0("ftpAcceptDataConn: returning -1\n");
  1134.     return(-1);
  1135.     }
  1136.     DEBUG1("ftpAcceptDataConn: closing port %d\n",ftpc->port);
  1137.     close(ftpc->port);
  1138.     ftpc->port = -1;
  1139.     DEBUG1("ftpAcceptDataConn: registering dataconn %d\n",datacon);
  1140.     ftpc->data = datacon;
  1141.     MAKE_NONBLOCKING(ftpc->data);
  1142.     RegisterFtpFd(ftpc,ftpc->data,O_RDONLY,ftpDataCallback);
  1143.     DEBUG1("ftpAcceptDataConn: returning dataconn = %d\n",datacon);
  1144.     return(datacon);
  1145. }
  1146.  
  1147. /*
  1148.  * Once the remote file is ready for transfer through the dataconn, this
  1149.  * function sets up the local end. Returns -1 on error, otherwise 0.
  1150.  */
  1151. static int
  1152. ftpSetupLocalData(ftpc)
  1153. FtpContext *ftpc;
  1154. {
  1155.     char filename[MAXPATHLEN];
  1156.  
  1157.     DEBUG1("ftpSetupLocalData: ftpc=0x%x\n",ftpc);
  1158.     if (ftpc->local_dir && *(ftpc->local_dir)) {
  1159.     sprintf(filename,"%s/%s",ftpc->local_dir,ftpc->files[ftpc->this_file]);
  1160.     } else {
  1161.     strcpy(filename,ftpc->files[ftpc->this_file]);
  1162.     }
  1163.     DEBUG1("ftpSetupLocalData: opening \"%s\"\n",filename);
  1164.     if (ftpc->filecmd == FTP_GET) {
  1165.     ftpc->local_fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,0644);
  1166.     } else {
  1167.     ftpc->local_fd = open(filename,O_RDONLY,0);
  1168.     }
  1169.     if (ftpc->local_fd < 0) {
  1170.     sysError(filename);
  1171.     DEBUG0("ftpSetupLocalData: returning -1\n");
  1172.     return(-1);
  1173.     }
  1174.     DEBUG1("ftpSetupLocalData: got local fd = %d\n",ftpc->local_fd);
  1175.     ftpc->num_bytes = 0;
  1176.     DEBUG0("ftpSetupLocalData: returning 0\n");
  1177.     return(0);
  1178. }
  1179.  
  1180. /*    -    -    -    -    -    -    -    -    */
  1181. /* Data connection processing: */
  1182. /*
  1183.  * This function is called whenever the dataconn is ready for read/write.
  1184.  */
  1185. static void
  1186. ftpDataCallback(ftpc)
  1187. FtpContext *ftpc;
  1188. {
  1189.     DEBUG2("ftpDataCallback: ftpc=0x%x, fcmd=%d\n",ftpc,ftpc->filecmd);
  1190.     switch (ftpc->filecmd) {
  1191.       case FTP_GET:
  1192.     ftpReadData(ftpc);
  1193.     break;
  1194.       case FTP_PUT:
  1195.     ftpWriteData(ftpc);
  1196.     break;
  1197.       default:
  1198.     fprintf(stderr,"ftpDataCallback: unknown cmd: %d\n",ftpc->filecmd);
  1199.     abort();
  1200.     }
  1201.     DEBUG0("ftpDataCallback: done\n");
  1202. }
  1203.  
  1204. /*
  1205.  * This function is called to read from the dataconn and write to the
  1206.  * local_fd. On EOF, it sets the state field to FTPS_EOF and calls
  1207.  * ftpInitReply() to continue the session. On error, it calls
  1208.  * ftpSendAbort() and then ftpInitReply(). Otherwise the transfer
  1209.  * is ongoing, so it doesn't do anything.
  1210.  */
  1211. static void
  1212. ftpReadData(ftpc)
  1213. FtpContext *ftpc;
  1214. {
  1215.     char buf[BUFSIZ*4],*s,*t,msg[256];
  1216.     int nread,i;
  1217.  
  1218.     DEBUG0("ftpReadData: reading\n");
  1219.     /* Read one chunk of stuff and write it locally (don't loop or
  1220.      * X won't get a chance to dispatch).
  1221.      */
  1222.     nread = read(ftpc->data,buf,sizeof(buf));
  1223.     if (nread > 0) {
  1224.     /* Strip CR's if necessary */
  1225.     if (ftpc->type == TYPE_A && ftpc->stripcr) {
  1226.         DEBUG0("ftpReadData: stripping <CR>\n");
  1227.         for (s=t=buf,i=nread; i--; ++s) {
  1228.         if (*s != '\r')
  1229.             *t++ = *s;
  1230.         }
  1231.     }
  1232.     /* Write locally */
  1233.     if (write(ftpc->local_fd,buf,nread) != nread) {
  1234.         /* Error when writing */
  1235.         sysError("write(ftpReadData)");
  1236.         ftpCleanupDataConn(ftpc);
  1237.         ftpSendAbort(ftpc);
  1238.         ftpInitReply(ftpc);
  1239.     } else {
  1240.         /* write was ok */
  1241.         ftpc->num_bytes += nread;
  1242.         DEBUG1("ftpReadData: total = %d\n",ftpc->num_bytes);
  1243.         if (ftpc->this_size != 0) {
  1244.         sprintf(msg,"%.200s: %d/%d",ftpc->files[ftpc->this_file],
  1245.             (char *)(ftpc->num_bytes),(char *)(ftpc->this_size));
  1246.         } else {
  1247.         sprintf(msg,"%.200s: %d bytes",ftpc->files[ftpc->this_file],
  1248.             (char *)(ftpc->num_bytes));
  1249.         }
  1250.         status0(msg);
  1251.     }
  1252.     } else if (nread < 0 && errno == ITWOULDBLOCK) {
  1253.     /* Nothing more ready now, keep waiting */
  1254.     DEBUG0("ftpReadData: dataconn would block\n");
  1255.     } else if (nread < 0) {
  1256.     /* Error when reading */
  1257.     sysError("read(ftpReadData)");
  1258.     DEBUG0("ftpReadData: error reading dataconn\n");
  1259.     ftpCleanupDataConn(ftpc);
  1260.     ftpSendAbort(ftpc);
  1261.     ftpInitReply(ftpc);
  1262.     } else if (nread == 0) {
  1263.     /* We got EOF on remote file. */
  1264.     DEBUG0("ftpReadData: EOF on dataconn\n");
  1265.     ftpCleanupDataConn(ftpc);
  1266.     if (ftpc->done1 != NULL)
  1267.         (*(ftpc->done1))(ftpc);
  1268.     ftpc->state = FTPS_EOF;
  1269.     ftpInitReply(ftpc);
  1270.     }
  1271. }
  1272.  
  1273. /*ARGSUSED*/
  1274. static void
  1275. ftpWriteData(ftpc)
  1276. FtpContext *ftpc;
  1277. {
  1278.     /*EMPTY*/
  1279. }
  1280.  
  1281. static void
  1282. ftpCleanupDataConn(ftpc)
  1283. FtpContext *ftpc;
  1284. {
  1285.     /* Note: We may have already unregistered, eg, in ftpAbort(). */
  1286.     if (ftpc->state != FTPS_ABORT2)
  1287.     UnregisterFtpFd(ftpc,ftpc->data);
  1288.     if (ftpc->data != -1) {
  1289.     DEBUG1("ftpCleanupDataConn: closing data %d\n",ftpc->data);
  1290.     close(ftpc->data);
  1291.     ftpc->data = -1;
  1292.     }
  1293.     if (ftpc->local_fd != -1) {
  1294.     DEBUG1("ftpCleanupDataConn: closing local_fd %d\n",ftpc->local_fd);
  1295.     close(ftpc->local_fd);
  1296.     ftpc->local_fd = -1;
  1297.     }
  1298. }
  1299.  
  1300. /*    -    -    -    -    -    -    -    -    */
  1301. /* Sending commands to the server: */
  1302. /*
  1303.  * Adds CRLF and calls to ftpStartSendingCmd().
  1304.  */
  1305. static void
  1306. ftpSendCmd(ftpc,cmd)
  1307. FtpContext *ftpc;
  1308. char *cmd;
  1309. {
  1310.     char buf[MAXPATHLEN];
  1311.  
  1312.     if (ftpc->trace) {
  1313.     (*(ftpc->trace))(ftpc,1,cmd);
  1314.     }
  1315.     sprintf(buf,"%s\r\n",cmd);
  1316.     ftpStartSendingCmd(ftpc,buf);
  1317. }
  1318.  
  1319. /*
  1320.  * Handles abort processing by sending the Telnet abort sequence in the
  1321.  * out-of-band stream of the ctrl conn and the ABOR command on the regular
  1322.  * stream.
  1323.  */
  1324. static void
  1325. ftpSendAbort(ftpc)
  1326. FtpContext *ftpc;
  1327. {
  1328.     char buf[8];
  1329.     int sent;
  1330.  
  1331.     DEBUG1("ftpSendAbort: ftpc=0x%lx\n",ftpc);
  1332.     DEBUG0("ftpSendAbort: sending <IAC><IP><IAC> in OOB\n");
  1333.     sprintf(buf,"%c%c%c",IAC,IP,IAC);
  1334.     do {
  1335.     sent = send(ftpc->ctrl,buf,3,MSG_OOB);
  1336.     } while (sent == -1 && errno == EINTR);
  1337.     if (sent != 3) {
  1338.     sysError("send(ftpSendAbort)");
  1339.     ftpDone(ftpc);
  1340.     }
  1341.     DEBUG0("ftpSendAbort: sending <DM>ABOR\n");
  1342.     sprintf(buf,"%cABOR\r\n",DM);
  1343.     do {
  1344.     sent = write(ftpc->ctrl,buf,strlen(buf));
  1345.     } while (sent == -1 && errno == EINTR);
  1346.     if (sent != strlen(buf)) {
  1347.     sysError("write(ftpSendAbort)");
  1348.     ftpDone(ftpc);
  1349.     }
  1350.     ftpc->state = FTPS_ABORT;
  1351.     ftpInitReply(ftpc);
  1352.     status0("Aborting...");
  1353.     DEBUG0("ftpSendAbort: done\n");
  1354. }
  1355.  
  1356. /*
  1357.  * This function starts sending CMD to the server over the control
  1358.  * connection. The writes can block, hence this has to be able to finish
  1359.  * later, but since they usually don't block, we don't set up to retry
  1360.  * unless we have to. If the command is completely sent, then we call
  1361.  * ftpInitReply() otherwise we setup to finish by registering the ctrlconn
  1362.  * for writing via callback ftpWriteCallback().
  1363.  */
  1364. static void
  1365. ftpStartSendingCmd(ftpc,cmd)
  1366. FtpContext *ftpc;
  1367. char *cmd;
  1368. {
  1369.     int cmdlen,numsent;
  1370.  
  1371.     DEBUG2("ftpStartSendingCmd: ftpc=0x%x: \"%s\"\n",ftpc,cmd);
  1372.     cmdlen = strlen(cmd);
  1373.     numsent = write(ftpc->ctrl,cmd,cmdlen);
  1374.     if (numsent < 0 && errno != ITWOULDBLOCK) {
  1375.     sysError("write(ftpStartSendingCmd)");
  1376.     DEBUG0("ftpStartSendingCmd: error writing ctrlconn\n");
  1377.     ftpDone(ftpc);
  1378.     return;
  1379.     } else if (numsent == cmdlen) {
  1380.     DEBUG0("ftpStartSendingCmd: write all done\n");
  1381.     ftpInitReply(ftpc);
  1382.     return;
  1383.     } else if (errno == ITWOULDBLOCK) {
  1384.     /* Otherwise the write would block, nothing sent */
  1385.     DEBUG0("ftpStartSendingCmd: write would block\n");
  1386.     numsent = 0;
  1387.     } else {
  1388.     /* Incomplete write */
  1389.     DEBUG1("ftpStartSendingCmd: write incomplete (%d bytes sent)\n",numsent);
  1390.     }
  1391.     /* Either way, if we get here there's something left do */
  1392.     cmd += numsent;
  1393.     if ((ftpc->cmd=malloc(strlen(cmd)+1)) == NULL) {
  1394.     sysError("malloc(ftpStartSendingCmd)");
  1395.     ftpDone(ftpc);
  1396.     return;
  1397.     }
  1398.     strcpy(ftpc->cmd,cmd);
  1399.     DEBUG1("ftpStartSendingCmd: pending: \"%s\"\n",ftpc->cmd);
  1400.     RegisterFtpFd(ftpc,ftpc->ctrl,O_WRONLY,ftpWriteCallback);
  1401. }
  1402.  
  1403. /*
  1404.  * This function is called when the ctrlconn is ready for writing.
  1405.  * It sends as much as possible. If that completes the cmd, then it
  1406.  * calls ftpInitReply(), otherwise updates the cmd with what's left
  1407.  * and returns.
  1408.  */
  1409. static void
  1410. ftpWriteCallback(ftpc)
  1411. FtpContext *ftpc;
  1412. {
  1413.     char *cmd;
  1414.     int cmdlen,numsent;
  1415.  
  1416.     cmd = ftpc->cmd;
  1417.     DEBUG2("ftpWriteCallback: ftpc=0x%x: \"%s\"\n",ftpc,cmd);
  1418.     cmdlen = strlen(ftpc->cmd);
  1419.     numsent = write(ftpc->ctrl,cmd,cmdlen);
  1420.     if (numsent < 0 && errno != ITWOULDBLOCK) {
  1421.     sysError("write(ftpWriteCallback)");
  1422.     DEBUG0("ftpWriteCallback: error writing ctrlconn\n");
  1423.     ftpDone(ftpc);
  1424.     return;
  1425.     } else if (numsent == cmdlen) {
  1426.     DEBUG0("ftpWriteCallback: write all done\n");
  1427.     UnregisterFtpFd(ftpc,ftpc->ctrl);
  1428.     free(ftpc->cmd);
  1429.     ftpInitReply(ftpc);
  1430.     return;
  1431.     } else if (errno == ITWOULDBLOCK) {
  1432.     /* Otherwise the write would block, nothing sent */
  1433.     DEBUG0("ftpWriteCallback: write would block\n");
  1434.     numsent = 0;
  1435.     } else {
  1436.     /* Incomplete write */
  1437.     DEBUG1("ftpWriteCallback: write incomplete (%d bytes sent)\n",numsent);
  1438.     }
  1439.     /* Either way, if we get here there's something left do */
  1440.     cmd += numsent;
  1441.     free(ftpc->cmd);
  1442.     if ((ftpc->cmd=malloc(strlen(cmd)+1)) == NULL) {
  1443.     sysError("malloc(ftpWriteCallback)");
  1444.     ftpDone(ftpc);
  1445.     return;
  1446.     }
  1447.     strcpy(ftpc->cmd,cmd);
  1448.     DEBUG1("ftpWriteCallback: pending: \"%s\"\n",ftpc->cmd);
  1449. }
  1450.  
  1451. /*    -    -    -    -    -    -    -    -    */
  1452. /* Printing utilities for debugging */
  1453.  
  1454. #ifdef DEBUG
  1455. char *
  1456. ftpstatestr(state)
  1457. int state;
  1458. {
  1459.     char buf[8];
  1460.  
  1461.     switch (state) {
  1462.     /* state */
  1463.       case FTPS_OPEN:        return("OPEN");
  1464.       case FTPS_CONNECT:    return("CONNECT");
  1465.       case FTPS_CONNECTED:    return("CONNECTED");
  1466.       case FTPS_USER:        return("USER");
  1467.       case FTPS_PASS:        return("PASS");
  1468.       case FTPS_CWD:        return("CWD");
  1469.       case FTPS_TYPE:        return("TYPE");
  1470.       case FTPS_READY:        return("READY");
  1471.       case FTPS_PORT:        return("PORT");
  1472.       case FTPS_GETPUT:        return("GETPUT");
  1473.       case FTPS_TRANSFER:    return("TRANSFER");
  1474.       case FTPS_EOF:        return("EOF");
  1475.       case FTPS_QUIT:        return("QUIT");
  1476.       case FTPS_ABORT:        return("ABORT");
  1477.       case FTPS_ABORT2:        return("ABORT2");
  1478.     /* reply_state */
  1479.       case FTPS_REPLY_CODE:    return("CODE");
  1480.       case FTPS_REPLY_CONT:    return("CONT");
  1481.       case FTPS_REPLY_LAST:    return("LAST");
  1482.       case FTPS_REPLY_MORE:    return("MORE");
  1483.       case FTPS_REPLY_CHCK:    return("CHCK");
  1484.       case FTPS_REPLY_IAC1:    return("IAC1");
  1485.       case FTPS_REPLY_IAC2:    return("IAC2");
  1486.       case FTPS_REPLY_IAC3:    return("IAC3");
  1487.       default:            sprintf(buf,"?%d?",state);
  1488.                 return(buf);
  1489.     }
  1490. }
  1491. #endif /* DEBUG */
  1492.  
  1493.  
  1494. /*    -    -    -    -    -    -    -    -    */
  1495. /* Sample code for standalone client */
  1496.  
  1497. #ifdef STANDALONE
  1498.  
  1499. /* This is from Brendan... */
  1500. #include <sys/types.h>        /* this may/will define FD_SET etc */
  1501. #ifdef u3b2
  1502. # include <sys/inet.h>        /* THIS does FD_SET etc on AT&T 3b2s.  */
  1503. #endif
  1504.  
  1505. fd_set readfds,writefds;
  1506. typedef void (*PF)();
  1507. PF funcs[FD_SETSIZE];
  1508. FtpContext *contexts[FD_SETSIZE];
  1509.  
  1510. void
  1511. RegisterFtpFd(ftpc,fd,flags,func)
  1512. FtpContext *ftpc;
  1513. int fd,flags;
  1514. void (*func)();
  1515. {
  1516.     switch (flags) {
  1517.       case O_RDONLY:
  1518.     fprintf(stderr,"REGISTER fd %d, flags=RO\n",fd);
  1519.     FD_SET(fd,&readfds);
  1520.     FD_CLR(fd,&writefds);
  1521.     break;
  1522.       case O_WRONLY:
  1523.     fprintf(stderr,"REGISTER fd %d, flags=WO\n",fd);
  1524.     FD_CLR(fd,&readfds);
  1525.     FD_SET(fd,&writefds);
  1526.     break;
  1527.       case O_RDWR:
  1528.     fprintf(stderr,"REGISTER fd %d, flags=RW\n",fd);
  1529.     FD_SET(fd,&readfds);
  1530.     FD_SET(fd,&writefds);
  1531.     break;
  1532.     }
  1533.     funcs[fd] = func;
  1534.     contexts[fd] = ftpc;
  1535. }
  1536.  
  1537. /*ARGSUSED*/
  1538. void
  1539. UnregisterFtpFd(ftpc,fd)
  1540. FtpContext *ftpc;
  1541. int fd;
  1542. {
  1543.     fprintf(stderr,"UNREGISTER fd %d\n",fd);
  1544.     FD_CLR(fd,&readfds);
  1545.     FD_CLR(fd,&writefds);
  1546.     funcs[fd] = NULL;
  1547.     contexts[fd] = NULL;
  1548. }
  1549.  
  1550. void
  1551. done(ftpc)
  1552. FtpContext *ftpc;
  1553. {
  1554.     fprintf(stderr,"DONE!\n");
  1555.     ftpFreeContext(ftpc);
  1556.     exit(0);
  1557. }
  1558.     
  1559. void
  1560. done1(ftpc)
  1561. FtpContext *ftpc;
  1562. {
  1563.     fprintf(stderr,"DONE1: \"%s\"\n",ftpc->files[ftpc->this_file]);
  1564. }
  1565.     
  1566. /*ARGSUSED*/
  1567. void
  1568. trace(ftpc,who,text)
  1569. FtpContext *ftpc;
  1570. int who;        /* 0 => recvd, non-0 => sent */
  1571. char *text;
  1572. {
  1573.     fprintf(stderr,"TRACE: ");
  1574.     if (who)    /* non-zero == us */
  1575.     fprintf(stderr,"ftp> ");
  1576.     fprintf(stderr,"%s",text);
  1577.     if (*(text+strlen(text)-1) != '\n')
  1578.     fprintf(stderr,"\n",text);
  1579. }
  1580.     
  1581. char *program;
  1582.  
  1583. main(argc,argv)
  1584. int argc;
  1585. char *argv[];
  1586. {
  1587.     char *host,*cwd;
  1588.     FtpContext *ftpc;
  1589.     fd_set rfds,wfds;
  1590.     int fd;
  1591.  
  1592.     if (argc < 3) {
  1593.     fprintf(stderr,"usage: %s host cwd files...\n",argv[0]);
  1594.     exit(1);
  1595.     }
  1596.     argc -= 1;
  1597.     program = *argv++;
  1598.     argc -= 1;
  1599.     host = *argv++;
  1600.     argc -= 1;
  1601.     cwd = *argv++;
  1602.     FD_ZERO(&rfds);
  1603.     FD_ZERO(&wfds);
  1604.     ftpc = ftpNewContext(host,"anonymous","ferguson@cs.rochester.edu",
  1605.              cwd,TYPE_I,1,1,FTP_GET,argv,argc,trace,done1,done);
  1606.     printf("Calling ftpStart...\n");
  1607.     ftpStart(ftpc);
  1608.     while (1) {
  1609.     bcopy((char *)&readfds,(char *)&rfds,sizeof(fd_set));
  1610.     bcopy((char *)&writefds,(char *)&wfds,sizeof(fd_set));
  1611.     fprintf(stderr,"select(): R=");
  1612.     for (fd=0; fd < FD_SETSIZE; fd++) {
  1613.         if (FD_ISSET(fd,&rfds))
  1614.         fprintf(stderr,"%d,",fd);
  1615.     }
  1616.     fprintf(stderr," W=");
  1617.     for (fd=0; fd < FD_SETSIZE; fd++) {
  1618.         if (FD_ISSET(fd,&wfds))
  1619.         fprintf(stderr,"%d,",fd);
  1620.     }
  1621.     fprintf(stderr,"\n");
  1622.     if (select(FD_SETSIZE,&rfds,&wfds,NULL,NULL) < 0) {
  1623.         perror(program);
  1624.         exit(1);
  1625.     }
  1626.     for (fd=0; fd < FD_SETSIZE; fd++) {
  1627.         if (FD_ISSET(fd,&rfds) || FD_ISSET(fd,&wfds)) {
  1628.         fprintf(stderr,"selected fd %d...\n",fd);
  1629.         (*(funcs[fd]))(contexts[fd]);
  1630.         }
  1631.     }
  1632.     }
  1633. }
  1634.  
  1635. void
  1636. sysError(s)
  1637. char *s;
  1638. {
  1639.     extern int errno;
  1640.     extern char *sys_errlist[];
  1641.     fprintf(stderr,"SYSERR: %s: %s\n",s,sys_errlist[errno]);
  1642. }
  1643.  
  1644. void
  1645. alert0(s)
  1646. char *s;
  1647. {
  1648.     fprintf(stderr,"ALERT: %s\n",s);
  1649. }
  1650.  
  1651. void
  1652. status0(s)
  1653. char *s;
  1654. {
  1655.     fprintf(stderr,"STATUS: %s\n",s);
  1656. }
  1657.  
  1658. int
  1659. ftpPrompt(ftpc)
  1660. FtpContext *ftpc;
  1661. {
  1662.     char c;
  1663.  
  1664.     fprintf(stderr,"CONFIRM: %s %s [ynaq]",
  1665.         ftpfilecmdstr(ftpc->filecmd),ftpc->files[ftpc->this_file]);
  1666.     c = getchar();
  1667.     if (c != '\n')
  1668.     (void)getchar();
  1669.     switch (c) {
  1670.       case 'y': return(1);
  1671.       case 'n': return(0);
  1672.       case 'a': ftpc->prompt = 0;
  1673.             return(1);
  1674.       case 'q': ftpc->this_file = ftpc->num_files;
  1675.             return(0);
  1676.       default: return(1);
  1677.     }
  1678. }
  1679. #endif /*STANDALONE*/
  1680.